////////////////////////////////////////////////////////////////////////////////
// Filename: modelclass.cpp
////////////////////////////////////////////////////////////////////////////////
#include "Model3D.h"

#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
#include <tinyxml.h>

#include <vector>
#include "TexturesManager.h"
#include "Tools.h"
#include "Game.h"
#include "Mesh.h"
#include "Bone.h"

Model3D::Model3D()
{
	mShader = 0;
	mMainMesh = NULL;
}


Model3D::Model3D(const Model3D& other)
{
}

Model3D Model3D::operator=(Model3D const&)
{
	return Model3D();
}

Model3D::~Model3D()
{
}

void Model3D::SetShader(BasicEffect* pShader)
{
	mShader = pShader;
}

void Model3D::Initialize(TiXmlElement* pModel3DElement)
{
	mPath = std::string(pModel3DElement->Attribute("path"));
	Load3DModelFile(std::string("Contents\\") + mPath);

	mId = ::atoi(pModel3DElement->Attribute("id"));

	mBones = new Bone*[mMeshesCount];
	for(int i = 0;i < mMeshesCount;i++)
	{
		mBones[i] = NULL;
	}

	TiXmlElement* boneElement = pModel3DElement->FirstChildElement( "Bone" );
	for( boneElement; boneElement; boneElement=boneElement->NextSiblingElement("Bone") )
	{
		Bone* bone = new Bone();
		bone->Initialize(boneElement);
		Mesh* mesh = GetMeshByName(bone->GetName());
		if(mesh != NULL)
		{
			mBones[mesh->GetIndex()] = bone;
			bone->SetMeshIndex(mesh->GetIndex());
			if(bone->IsMain())
			{
				mMainMesh = mesh;
			}
			mesh->SetBone(bone);			
		}
	}

	TiXmlElement* meshElement = pModel3DElement->FirstChildElement( "Mesh" );
	for( meshElement; meshElement; meshElement=meshElement->NextSiblingElement("Mesh") )
	{
		std::string meshName = std::string(meshElement->Attribute("name"));
		Mesh* mesh = GetMeshByName(meshName);

		const char* cBoneName = meshElement->Attribute("bone");
		if(cBoneName != NULL)
		{
			std::string boneName = std::string(cBoneName);	
			Bone* bone = GetBoneByName(boneName);
			mesh->SetBone(bone);
		}

		const char* cOrigine = meshElement->Attribute("origine");
		if(cOrigine != NULL)
		{
			XMFLOAT3 origine;
			GetVector3D(std::string(meshElement->Attribute("origine")), origine);
			mesh->SetOrigine(origine);
		}
	}


	for(int i = 0;i < mMeshesCount;i++)
	{
		if(mBones[i] == NULL)continue;
		std::string parentName = mBones[i]->GetParentName();
		if(parentName == "")continue;
		Bone* bone = GetBoneByName(parentName);
		mBones[i]->SetParent(bone);
	}
}

void Model3D::Load3DModelFile(std::string pFile)
{
	if(mShader == 0)
	{
		mShader = Game::Instance()->GetShader();
	}

	mDirectory = GetDirectoryPath(pFile);

	// Initialize the vertex and index buffers.
	Assimp::Importer Importer;

    mScene = Importer.ReadFile(pFile, aiProcess_Triangulate| aiProcess_FlipUVs | aiProcess_GenSmoothNormals);// | aiProcess_GenSmoothNormals | aiProcess_FlipUVs);

	std::vector<Mesh*> vMeshes;
	unsigned int meshesCount = mScene->mNumMeshes;
	for (unsigned int i = 0 ; i < meshesCount ; i++) {
        const aiMesh* paiMesh = mScene->mMeshes[i];
		Mesh* mesh = new Mesh();
		mesh->Initialize(paiMesh, GetTexture(paiMesh->mMaterialIndex), mShader);
		vMeshes.push_back(mesh);
    }

	mMeshesCount = vMeshes.size();
	mMeshes = new Mesh*[mMeshesCount];
	for(int i = 0;i < mMeshesCount;i++)
	{
		mMeshes[i] = vMeshes[i];
		mMeshes[i]->SetIndex(i);
	}
}

Bone* Model3D::GetBoneByMeshIndex(int pIndex)
{
	return mBones[pIndex];
}

Bone* Model3D::GetBoneByName(std::string pName)
{
	for(int i = 0;i < mMeshesCount;i++)
	{
		if(mBones[i] == NULL)continue;
		if(mBones[i]->GetName() == pName)
		{
			return mBones[i];
		}
	}
	return NULL;
}

Mesh* Model3D::GetMeshByName(std::string pName)
{
	for(int i = 0;i < mMeshesCount;i++)
	{
		std::string meshName = mMeshes[i]->GetName();
		if(meshName == pName)
		{
			return mMeshes[i];
		}
	}
	return NULL;
}

std::string Model3D::GetPath()
{
	return mPath;
}

int Model3D::GetId()
{
	return mId;
}

Texture* Model3D::GetTexture(unsigned int pMaterialIndex)
{
	aiString Path;
	if(mScene->mMaterials[pMaterialIndex]->GetTexture(aiTextureType_DIFFUSE, 0, &Path, NULL, NULL, NULL, NULL, NULL) == AI_SUCCESS) 
	{
		return TexturesManager::Instance()->GetTexture(std::string(Path.C_Str()));
	}
	else
	{
		return TexturesManager::Instance()->GetTexture("SHADOW");
	}
}

void Model3D::Render(const XMFLOAT4X4 &worldMatrix, const XMFLOAT4X4 &viewMatrix, const XMFLOAT4X4 &projectionMatrix)
{
	for(int index = 0;index < mMeshesCount;++index)
	{
		mMeshes[index]->Render(worldMatrix, viewMatrix, projectionMatrix);
	}
	return;
}

//void Model3D::ShadowRender(ShadowMap* pShadowMap, const XMFLOAT4X4 &worldMatrix)
//{
//	for(int index = 0;index < mMeshesCount;++index)
//	{
//		mMeshes[index]->ShadowRender(pShadowMap, worldMatrix);
//	}
//	return;
//}

void Model3D::Dispose()
{
	// Release the index buffer.
	//for(unsigned int i = 0;i < m_meshesCount;i++)
	//{
	//	m_meshes[i].Dispose();
	//}

	return;
}


Mesh* Model3D::GetMeshByIndex(int pIndex)
{
	return mMeshes[pIndex];
}

int Model3D::GetMeshesCount()
{
	return mMeshesCount;
}